const sRoot = __dirname;
const osFs = require('fs');
const osPath = require('path');
const { spawn: osSpawn } = require('child_process');
const oEJS = require('ejs');
const { fnWriteFile } = require('../utils/fs');

const { detectClones } = require("jscpd");
const { MemoryStore, Statistic } = require("@jscpd/core");
const { InFilesDetector, getFilesToDetect, JsonReporter } = require("@jscpd/finder");
const { Tokenizer } = require("@jscpd/tokenizer");

class JscpdChecker {
	constructor() {
		this.oDuplicatedLines = {};
		this.aMatchSourceEvent = [];
		this.aTempClones = [];
	}

	async fnCheck({ aFileList, oCheckOptions, sReportPath, sRootPath }) {
		const {
			iJscpdMinLines,
			iJscpdMinTokens,
			iJspcdMaxLines,
			iJspcdMaxSize,
			iThresholdJscpd,
			sJscpdExts,
			sFormat,
			sExt,
		} = oCheckOptions;

		const jscpdOptions = {
			silent: true,
			minLines: iJscpdMinLines, // jscpd Default: 5
			maxLines: iJspcdMaxLines, // jscpd Default: 500
			maxSize: iJspcdMaxSize, // jscpd Default: 30kb
			minTokens: iJscpdMinTokens, // jscpd Default: 50
			path: [...aFileList],
		};

		if (!!iThresholdJscpd) {
			jscpdOptions['threshold'] = iThresholdJscpd;
		}
		if(sFormat === 'json') {
			jscpdOptions['output'] = sReportPath;
		}
		// jscpdOptions['format'] = sJscpdExts.join(",");
		jscpdOptions['formats-exts'] = sJscpdExts.join(";");

		const ONStore = new MemoryStore();
		const iTotalLines = await this.getTotalLines(aFileList);		

		const aJscpdRes = await detectClones(jscpdOptions, ONStore);


		if(sFormat === 'html') {
			const oCompuateEventDatas = this.fnCompuateEventDatas(aJscpdRes);

			// 匹配总行数
			const iTotals = iTotalLines;

			// 匹配文件数
			const iMacthFiles = aFileList.length;

			// 覆盖项数
			const iDuplicatedLines = this.fnGetDuplicatedLines(aJscpdRes) * 2;

			const aSourceSkippedEvent = aJscpdRes;

			this.fnOutputHtml({sReportPath, sRootPath, oCompuateEventDatas, iTotals, iMacthFiles, aSourceSkippedEvent, iDuplicatedLines})
		} else {
			this.fnWriteSkipped(sReportPath, aSourceSkippedEvent);
		}

		return 'success';
	}

	fnWriteSkipped(sReportPath, aSourceSkippedEvent){
		fnWriteFile(
			osPath.join(sReportPath, 'jscpd-skipped.json'),
			JSON.stringify( {"skipped": aSourceSkippedEvent }, null, 2)
		)
		return true;
	}

	fnCompuateEventDatas(aRes) {
		let newData = {};
		aRes.forEach( (oItem) => {
			if(newData[oItem['format']]){
				newData[oItem['format']].push(oItem);
			}else{
				newData[oItem['format']] = [].concat(oItem);
			}
		});
		
		return newData;
	}

	async fnOutputHtml({sReportPath, sRootPath, oCompuateEventDatas, iMacthFiles, iDuplicatedLines, iTotals, aSourceSkippedEvent}){
		const sTplPath = osPath.join(sRoot, '../html-tpl/index.html');
    const sOutPutPath  = osPath.join(sReportPath, 'index.html');

		const aSeriesData = [];
		Object.keys(oCompuateEventDatas).forEach((sKey) => {
			aSeriesData.push({'value': oCompuateEventDatas[sKey].length, 'name': sKey});
		});
		
		await oEJS.renderFile(sTplPath, {
			sRootPath: sRootPath,
			aFormats: Object.keys(oCompuateEventDatas),
			aSeriesData,
			oCompuateEventDatas,
			iDuplicatedLines,
			iTotals,
			iMacthFiles,
			aSourceSkippedEvent,
    }, function (oErr, sData) {
      if (oErr) { throw new Error(oErr); }
      fnWriteFile(sOutPutPath, sData);
		});
		return true;
	}

	fnGetTotalLinesOfCode(aVal) {
		return aVal.reduce((iAcc, oCur) => {
      return iAcc + oCur['lines']
    }, 0);
  }
  
  fnGetDuplicatedLines(aVal){
		return aVal.reduce((iAcc, oCur) => {
      const { start: oStartA, end: oEndA } = oCur.duplicationA;
      return iAcc + oEndA.line - oStartA.line;
    }, 0);
  }


	async getLines(path) {
		const rep = await osFs.readFileSync(path).toString();
		const lines = rep.split('\n');
		// 匹配出注释的行数
		const commentNum = lines.filter(line => new RegExp('^(//|/\\*|\\*|\\*/)', 'g').test(line.trimStart())).length;
		return lines.length - commentNum;
	}

	getTotalLines(aFileList) {
		return new Promise((resolve) => {			
			const curVal = [];
			aFileList.forEach((sItem, index) => {
				curVal[index] = this.getLines(sItem);
			});
			Promise.all(curVal).then((res) => {
				resolve(res.reduce((pre, cur) => pre+cur, 0));
			})
		})
	}
}

module.exports = JscpdChecker;
